import Mn from 'backbone.marionette';

import {
    getPercent,
    round
} from "math.js"
import {
    ajax_call
} from 'tools.js';
import FacadeModelApiMixin from '../../base/components/FacadeModelApiMixin.js';

import FileRequirementCollection from '../models/FileRequirementCollection.js';
import CommonModel from "../models/CommonModel.js";
import GeneralModel from "../models/GeneralModel.js";
import ExpenseHtModel from "../models/ExpenseHtModel.js";
import NotesModel from "../models/NotesModel.js";
import PaymentConditionsModel from "../models/PaymentConditionsModel.js";
import TaskGroupCollection from '../models/TaskGroupCollection.js';

import DiscountCollection from '../models/DiscountCollection.js';
import PostTTCLineCollection from '../models/PostTTCLineCollection.js';
import PaymentLineCollection from '../models/PaymentLineCollection.js';
import TotalModel from '../models/TotalModel.js';

import RelatedEstimationCollection from '../models/RelatedEstimationCollection.js';
import PaymentOptionsModel from '../models/PaymentOptionsModel.js';
import StatusLogEntryCollection
from "../../common/models/StatusLogEntryCollection";
import DisplayOptionsModel from '../models/DisplayOptionsModel.js';
import AttachedFileCollection from '../models/AttachedFileCollection.js';
import PriceStudyModel from '../models/price_study/PriceStudyModel.js';
import PriceStudyChapterCollection from '../models/price_study/ChapterCollection.js';
import PriceStudyDiscountCollection from '../models/price_study/DiscountCollection';
import ProgressInvoicingChapterCollection from '../models/progress_invoicing/ChapterCollection.js';


/**
 * Task Form Facade
 * 
 * Manage all the models and collections of the interface
 * - Document
 * - PaymentLines
 * - TaskLineGroups or ProgressInvoicingLines or PriceStudy data
 * - Discounts
 * - PostTTCLines
 * - FileRequirements
 * - Attached Files
 * 
 * 
 * Process the setup of models collections and load the data
 * 
 * 1- setup is called to configure the urls ...
 * 2- start
 *      - onBeforeStart -> Initialize the models/collections and set the urls
 *      - query the Data
 *      - onAfterStart : apply the loaded data to the different Task models
 */
const FacadeClass = Mn.Object.extend(FacadeModelApiMixin).extend({
    ht: 5,
    radioEvents: {
        'changed:task': 'computeTotals',
        'changed:discount': 'computeTotals',
        'changed:post_ttc_line': 'computeTotals',
        'changed:expense_ht': 'computeTotals',
        'changed:date': "onDateChanged",
    },
    radioRequests: {
        'get:model': 'getModelRequest',
        'get:collection': 'getCollectionRequest',
        'is:valid': "isDataValid",
        'load:model': 'loadModel',
        'load:collection': 'loadCollection',
        'save:model': 'saveModel',
        'save:all': 'saveAll'
    },
    initialize() {
        this.syncModel = this.syncModel.bind(this);
        this.models = {};
        this.collections = {};
    },
    setup(options) {
        /*
         * Lancé avant tout appel ajax, options : les options passées depuis la
         * page html
         */
        console.log("Facade.setup");
        this.app_options = _.clone(options);
    },
    /**
     * Run on application startup, initialize all models that will be fetched before Building the ui
     */
    onBeforeStart(options) {
        console.log("Facade.onBeforeStart")
        if ('task_line_group_api_url' in options) {
            this.collections['task_groups'] = new TaskGroupCollection();
            this.collections.task_groups.url = options['task_line_group_api_url'];
            if ('discount_api_url' in options) {
                this.collections['discounts'] = new DiscountCollection();
                this.collections['discounts'].url = options['discount_api_url'];
            }
        } else if ("price_study_url" in options) {
            this.models['price_study'] = new PriceStudyModel();
            this.models['price_study'].url = options["price_study_url"];
            this.collections['price_study_chapters'] = new PriceStudyChapterCollection();
            this.collections['price_study_chapters'].url = options['price_study_chapters_url'];
        } else if ("progress_invoicing_chapters_url" in options) {
            this.collections['progress_invoicing_chapters'] = new ProgressInvoicingChapterCollection();
            this.collections['progress_invoicing_chapters'].url = options['progress_invoicing_chapters_url']
        }

        // On peut avoir les deux du coup on s'assure d'utiliser celle des études si étude il y a
        if ('price_study_discount_api_url' in options) {
            this.collections['discounts'] = new PriceStudyDiscountCollection();
            this.collections['discounts'].url = options['price_study_discount_api_url'];
        } else if ('discount_api_url' in options) {
            this.collections['discounts'] = new DiscountCollection();
            this.collections['discounts'].url = options['discount_api_url'];
        }
        if ('post_ttc_api_url' in options) {
            this.collections['post_ttc_lines'] = new PostTTCLineCollection();
            this.collections['post_ttc_lines'].url = options['post_ttc_api_url'];
        }

        if ("payment_lines_api_url" in options) {
            this.collections['payment_lines'] = new PaymentLineCollection();
            this.collections['payment_lines'].url = options["payment_lines_api_url"];
        }
        if ("file_requirement_url" in options) {
            this.collections['file_requirements'] = new FileRequirementCollection();
            this.collections['file_requirements'].url = options['file_requirement_url'];
        }
        if ("file_attachment_url" in options) {
            this.collections['attached_files'] = new AttachedFileCollection();
            this.collections['attached_files'].url = options['file_attachment_url'];
        }

        if ('related_estimation_url' in options) {
            this.collections['related_estimations'] = new RelatedEstimationCollection();
            this.collections['related_estimations'].url = options['related_estimation_url'];
        }
        this.models['total'] = new TotalModel();
        this.models['total'].url = options['total_url'];
    },
    start() {
        console.log("Starting the facade");
        this.onBeforeStart(this.app_options)
        console.log("Loading document data");
        const load_document_request = ajax_call(this.app_options['context_url']);
        let requests = [load_document_request];
        for (const name in this.models) {
            console.log("Loading model %s", name)
            requests.push(this.loadModel(name))
        }
        for (const name in this.collections) {
            console.log("Loading collection %s", name)
            requests.push(this.loadCollection(name));
        }

        return $.when(...requests).then(this.onAfterStart.bind(this));
    },
    _setupPaymentLinesEvents() {
        const collection = this.collections['payment_lines'];
        // On update la collection lors de ces évènements
        this.listenTo(collection, 'saved', () => collection.fetch());
        this.listenTo(this.models['total'], 'change:ttc', () => {
            this.models['payment_options'].fetch();
            collection.fetch();
        });
        this.listenTo(this.models['payment_options'], 'saved:deposit', () => collection.fetch());
        this.listenTo(this.models['payment_options'], 'saved:payment_times', () => collection.fetch());
    },
    /**
     * Setup task models 
     * 
     * Models that are populated from the main context api call and are then autonomous to edit part of it
     */
    _setupTaskModels() {
        // Task models (submodels handling only part of the attributes,     
        // associated to different view components)
        this.models['general'] = new GeneralModel();
        this.models['general'].url = this.app_options['context_url'];
        this.models['common'] = new CommonModel();
        this.models['common'].url = this.app_options['context_url'];
        this.models['display_options'] = new DisplayOptionsModel();
        this.models['display_options'].url = this.app_options['context_url'];
        this.models['expense_ht'] = new ExpenseHtModel();
        this.models['expense_ht'].url = this.app_options['context_url'];
        this.models['notes'] = new NotesModel();
        this.models['notes'].url = this.app_options['context_url'];

        // #Todo : ne pas instancier les modèles ci-dessous si ils ne sont pas utilisés
        this.models['payment_conditions'] = new PaymentConditionsModel();
        this.models['payment_conditions'].url = this.app_options['context_url'];
        // Estimation only model
        this.models['payment_options'] = new PaymentOptionsModel();
        this.models['payment_options'].url = this.app_options['context_url'];
    },
    onAfterStart(loaded_datas) {
        this._setupTaskModels()

        const context_datas = loaded_datas[0];
        console.log("setup Context Models");
        console.log(context_datas);

        ['general', 'common', 'display_options', 'expense_ht', 'notes', 'payment_conditions', 'payment_options'].map(
            (modelName) => {
                this.models[modelName].set(context_datas);
                this.models[modelName].changed = {};
            }
        );
        if ('payment_lines' in this.collections) {
            this._setupPaymentLinesEvents();
        }
        if (_.has(context_datas, 'status_history')) {
            var history = context_datas['status_history'];
            this.collections['status_history'] = new StatusLogEntryCollection(history);
        }
        this.computeTotals();
        this.listenTo(this.models.display_options, 'saved:input_mode', () => window.location.reload());
    },
    syncModel(modelName) {
        var modelName = modelName || 'common';
        return this.models[modelName].save(null, {
            wait: true,
            sync: true,
            patch: true
        });
    },
    computeTotals() {
        console.log("Computing totals");
        this.models.total.fetch({
            async: false
        });
        console.log("Total retrieved");
        // const new_values = {
        //     'ht_before_discounts': this.HTBeforeDiscounts(),
        //     'ttc_before_discounts': this.TTCBeforeDiscounts(),
        //     'ht': this.HT(),
        //     'tvas': this.TVAParts(),
        //     'ttc': this.TTC(),
        // }
    },
    tasklines_ht() {
        return this.collections['task_groups'].ht();
    },
    getComputeCollections() {
        const result = []
        for (const name in this.collections) {
            if (this.collections[name].ht) {
                result.push(this.collections[name]);
            }
        }
        return result;
    },
    getComputeModels() {
        return [this.models['expense_ht']];
    },
    HTBeforeDiscounts() {
        let result = this.HT();
        if ('discounts' in this.collections) {
            result = result - this.collections.discounts.ht();
        }
        return result;
    },
    TTCBeforeDiscounts() {
        let result = this.TTC();
        if ('discount' in this.collections) {
            result = result - this.collections.discounts.ttc();
        }
        return result;
    },
    HT() {
        var result = 0;
        _.each(this.getComputeCollections(), function (collection) {
            result += collection.ht();
        });
        _.each(this.getComputeModels(), function (model) {
            result += model.ht();
        });
        console.log("Computing HT : %s", result)
        return result;
    },
    TVAParts() {
        var result = {};
        _.each(this.getComputeCollections(), function (collection) {
            var tva_parts = collection.tvaParts();
            _.each(tva_parts, function (value, key) {
                if (key in result) {
                    value += result[key];
                }
                result[key] = value;
            });
        });
        _.each(this.getComputeModels(), function (model) {
            var tva_parts = model.tvaParts();
            _.each(tva_parts, function (value, key) {
                if (key in result) {
                    value += result[key];
                }
                result[key] = value;
            });
        });
        return result;
    },
    TTC() {
        var result = round(this.HT());
        _.each(this.TVAParts(), function (value) {
            result += round(value);
        });
        return result;
    },
    insurance(totalHt) {
        const rate = this.models.common.getInsuranceRate()
        let result = null;
        if (rate) {
            result = getPercent(totalHt, rate)
        }
        return result;
    },
    onDateChanged() {
        this.models.general.fetch();
    }
});
const Facade = new FacadeClass();
export default Facade;
